/*
 * Copyright ?2008 Nokia Corporation.
 */

// INCLUDE FILES
#include "SMSExampleMtmsEngine.h"
#include "SmsEnginePanics.pan"

// SYSTEM FILES
#include <f32file.h>        // TParsePtrC
#include <mmsclient.h>      // CMmsClientMtm
#include <mtclreg.h>        // CClientMtmRegistry
#include <mtmdef.h>         // KMsvMessagePartBody
#include <smsclnt.h>        // CSmsClientMtm
#include <smscmds.h>        // ESmsMtmCommandScheduleCopy
#include <smutset.h>        // CSmsSettings
#include <smuthdr.h>        // CSmsHeader
#include <txtrich.h>        // CRichText
#include <eikenv.h>

const TInt KMessageAddressLength = 100;
const TInt KMessageBodySize = 512;
const TInt KArrayGranularity=10;

_LIT(KSenderJohnDoe,"*Unknown*");

CSMSExampleMtmsEngine* CSMSExampleMtmsEngine::NewL(
                                      MSMSExampleMtmsEngineObserver& aObserver)
    {
    CSMSExampleMtmsEngine* self =
                           new (ELeave) CSMSExampleMtmsEngine(aObserver);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }

CSMSExampleMtmsEngine::CSMSExampleMtmsEngine(
                                      MSMSExampleMtmsEngineObserver& aObserver)
: CActive(0),
  iObserver(aObserver),
  iSmsId(KMsvNullIndexEntryId)
    {

    }

void CSMSExampleMtmsEngine::ConstructL()
    {
    CActiveScheduler::Add(this);

    // iEntrySelection encapsulates an array of entry IDs
    iEntrySelection = new (ELeave) CMsvEntrySelection;

    // Represents a channel of communication between a client thread
    // (Client-side MTM, User Interface MTM, or message client application)
    // and the Message Server thread.
    // Session is opened asynchorously. CreateMtmClientL() is called afterwards.
    // Another possibility is use OpenSyncL which is synchronous.
    iSession = CMsvSession::OpenAsyncL(*this);
    }

// Creates CSmsClientMtm after session has been opened.
void CSMSExampleMtmsEngine::CreateMtmClientL()
    {
    // Client-side MTM registry.
    iClientMtmReg = CClientMtmRegistry::NewL(*iSession);

    //Note: If capabilities are missing, then iSmsMtm stays null
    // Get the SMS Mtm client from the registry
    iSmsMtm = static_cast<CSmsClientMtm*>(iClientMtmReg->NewMtmL(KUidMsgTypeSMS));
    }

CSMSExampleMtmsEngine::~CSMSExampleMtmsEngine()
    {
    Cancel();
    delete iMsvOper;
    delete iEntrySelection;
    delete iSmsMtm;
    delete iClientMtmReg;
    delete iSession;
    }

void CSMSExampleMtmsEngine::DoCancel()
    {
    if (iMsvOper)
        {
        iMsvOper->Cancel();
        delete iMsvOper;
        iMsvOper = NULL;
        }
    }

void CSMSExampleMtmsEngine::RunL()
    {
#ifdef __SERIES60_3X__
		iObserver.HandleMessageSentL(iStatus.Int());
#else
		delete iMsvOper;
		iMsvOper = NULL;

		iObserver.HandleMessageSentL(iStatus.Int());

		// Determine which request has finished.
		switch ( iState )
		{
		case EWaitingForMoving:
			// Once a message is moved to Outbox it is scheduled for sending.
			ScheduleL();
			break;

		case EWaitingForScheduling:
			{
				TMsvEntry entry( iSmsMtm->Entry().Entry() );
				TInt state( entry.SendingState() );

				if ( state == KMsvSendStateWaiting || state == KMsvSendStateScheduled)
				{
					/*              HBufC* text = StringLoader::LoadLC( R_SMS_MOVED_TO_OUTBOX );
					CAknInformationNote* informationNote =
					new ( ELeave ) CAknInformationNote;
					informationNote->ExecuteLD( *text );
					CleanupStack::PopAndDestroy( text );*/
				}

				break;
			}

		default:
			break;
		}
#endif
    }

#ifndef __SERIES60_3X__

	void CSMSExampleMtmsEngine::AccessMtmL()
	{
		// Create an MTM Registry object.
		iClientMtmReg = CClientMtmRegistry::NewL( *iSession );

		// Create an SMS Client MTM object.
		iSmsMtm = STATIC_CAST( CSmsClientMtm*, iClientMtmReg->NewMtmL( KUidMsgTypeSMS ) );
	}

	TBool CSMSExampleMtmsEngine::SendL( const TDesC& aRecipientNumber,
		const TDesC& aMessageText )
	{
		iRecipientNumber = aRecipientNumber;
		iMessageText = aMessageText;

		if ( CreateMsgL() )
		{
			return ETrue;
		}

		return EFalse;
	}

	void CSMSExampleMtmsEngine::ScheduleL()
	{
		CMsvEntrySelection* selection = new ( ELeave ) CMsvEntrySelection;
		CleanupStack::PushL( selection );
		selection->AppendL( iSmsMtm->Entry().EntryId() ); // add message to selection

		// Add entry to task scheduler.
		TBuf8<1> dummyParams;   // dummy parameters needed for InvokeAsyncFunctionL
		iMsvOper = iSmsMtm->InvokeAsyncFunctionL( ESmsMtmCommandScheduleCopy,
			*selection, dummyParams, iStatus );

		CleanupStack::PopAndDestroy( selection );

		iState = EWaitingForScheduling;
		SetActive();
	}

	TBool CSMSExampleMtmsEngine::CreateMsgL()
	{
		// Current entry is the Draft folder.
		iSmsMtm->SwitchCurrentEntryL( KMsvDraftEntryId );
		//iSmsMtm->SwitchCurrentEntryL( KMsvGlobalOutBoxIndexEntryId );

		// Create a new SMS message entry as a child of the current context.
		iSmsMtm->CreateMessageL( KUidMsgTypeSMS.iUid );

		CMsvEntry& serverEntry = iSmsMtm->Entry();
		TMsvEntry entry( serverEntry.Entry() );

		CRichText& body = iSmsMtm->Body();   // the body of the message
		body.Reset();
		// Insert the message text gotten as input from user.
		body.InsertL( 0, iMessageText );

		// Message will be sent immediately.
		entry.SetSendingState( KMsvSendStateWaiting );
		entry.iDate.HomeTime(); // insert current time

		// Set the SMS message settings for the message.
		CSmsHeader& header = iSmsMtm->SmsHeader();
		CSmsSettings* settings = CSmsSettings::NewL();
		CleanupStack::PushL( settings );

		settings->CopyL( iSmsMtm->ServiceSettings() );    // restore settings
		settings->SetDelivery( ESmsDeliveryImmediately ); // to be delivered immediately
		header.SetSmsSettingsL( *settings );              // new settings

		// Let's check if there is a service center address.
		if ( header.Message().ServiceCenterAddress().Length() == 0 )
		{
			// No, there isn't. We assume there is at least one service center
			// number set and use the default service center number.
			CSmsSettings* serviceSettings = &( iSmsMtm->ServiceSettings() );

			// Check if number of service center addresses in the list is null.
			if ( !serviceSettings->NumSCAddresses() )
			{
				/*            HBufC* text = StringLoader::LoadLC( R_SMS_SC_NBR_MISSING );
				CAknWarningNote* warningNote = new ( ELeave ) CAknWarningNote;
				warningNote->ExecuteLD( *text );
				CleanupStack::PopAndDestroy( text );
				*/
				// Remember to pop settings even if this branch is taken.
				CleanupStack::PopAndDestroy( settings );
				return EFalse;     // quit creating the message
			}

			else
			{
				// Set service center address to default.
				// The caller does not take ownership of the returned object.
				CSmsNumber* smsCenter =
					&( serviceSettings->SCAddress( serviceSettings->DefaultSC() ) );
				header.Message().SetServiceCenterAddressL( smsCenter->Address() );
			}
		}

		CleanupStack::PopAndDestroy( settings );

		// Recipient number is displayed also as the recipient alias.
		entry.iDetails.Set( iRecipientNumber );
		// Add addressee.
		iSmsMtm->AddAddresseeL( iRecipientNumber, entry.iDetails );

		// Validate message.
		if ( !ValidateL() )
		{
			return EFalse;
		}

		entry.SetVisible( ETrue );          // set message as visible
		entry.SetInPreparation( EFalse );   // set together with the visibility flag
		serverEntry.ChangeL( entry );       // commit changes		
		iSmsMtm->SaveMessageL();            // save message

		TMsvSelectionOrdering selection;
		CMsvEntry* parentEntry =
			CMsvEntry::NewL( iSmsMtm->Session(), KMsvDraftEntryId, selection );
		CleanupStack::PushL( parentEntry );

		// Move message to Outbox.
		iMsvOper =
			parentEntry->MoveL( entry.Id(), KMsvGlobalOutBoxIndexEntryId, iStatus );

		CleanupStack::PopAndDestroy( parentEntry );

		iState = EWaitingForMoving;
		SetActive();

		return ETrue;
	}


	TBool CSMSExampleMtmsEngine::ValidateL()
	{
		// Empty part list to hold the result.
		TMsvPartList result( KMsvMessagePartNone );

		// Validate message body.
		result = iSmsMtm->ValidateMessage( KMsvMessagePartBody );

		if ( result != KMsvMessagePartNone )
		{
			/*      HBufC* text = StringLoader::LoadLC( R_SMS_MSG_BODY );
			CAknWarningNote* warningNote = new ( ELeave ) CAknWarningNote;
			warningNote->ExecuteLD( *text );
			CleanupStack::PopAndDestroy( text ); */
			return EFalse;
		}

		// Validate recipient.
		result = iSmsMtm->ValidateMessage( KMsvMessagePartRecipient );

		if ( result != KMsvMessagePartNone )
		{
			/*      HBufC* text = StringLoader::LoadLC( R_SMS_RECIPIENT_NBR );
			CAknWarningNote* warningNote = new ( ELeave ) CAknWarningNote;
			warningNote->ExecuteLD( *text );
			CleanupStack::PopAndDestroy( text );*/
			return EFalse;
		}

		return ETrue;
	}
#endif

// Move message to folder. Notice that the iSmsMtm points to the parent, not
// to the message itself. If you move messages from inbox to drafts, those
// messages cannot be edited because their complete flag is true.
void CSMSExampleMtmsEngine::MoveToFolderL( TMsvId aMessageId,  TMsvId aFolder )
    {
    iSmsMtm->SwitchCurrentEntryL( aMessageId );
    TMsvSelectionOrdering selection;
    selection.SetShowInvisibleEntries(ETrue);
    CMsvEntry* parentEntry = CMsvEntry::NewL( iSmsMtm->Session(),
        iSmsMtm->Entry().Entry().Parent(), selection );
    CleanupStack::PushL(parentEntry);
    // Move the message
    parentEntry->MoveL( aMessageId, aFolder );
    CleanupStack::PopAndDestroy(parentEntry); // parentEntry
    }

// Delete message from a folder. Notice that the iSmsMtm points to the parent,
// not to the message itself.
void CSMSExampleMtmsEngine::DeleteMessageL( TMsvId aMessageId )
    {
    iSmsMtm->SwitchCurrentEntryL( aMessageId );
    TMsvId parent = iSmsMtm->Entry().Entry().Parent();

    iSmsMtm->SwitchCurrentEntryL( parent );
    iSmsMtm->Entry().DeleteL( aMessageId );
    }

// Get originator address from SmsHeader.
void CSMSExampleMtmsEngine::GetMessageAddressL( TMsvId aMessageId,
                                                TDes& aAddress )
    {
    iSmsMtm->SwitchCurrentEntryL( aMessageId );

    // Remember to load before using the SmsHeader
    iSmsMtm->LoadMessageL();

    CSmsHeader& header = iSmsMtm->SmsHeader();

    aAddress.Append( header.FromAddress() );
    // Other possibility is this: (It's little bit faster than the previous one).
    // aAddress.Append( iSmsMtm->Entry().Entry().iDetails );
    }

// Get message body from entry storage (CMsvStore). CMsvStore stores whole
// message, not summary information ( see GetMessageIndexBodyTextL() ).
TBool CSMSExampleMtmsEngine::GetMessageL( TMsvId aMessageId, TDes& aMessage)
    {
    iSmsMtm->SwitchCurrentEntryL( aMessageId );

    if ( iSmsMtm->Entry().HasStoreL() )
        {
        // SMS message is stored inside Messaging store.
        CMsvStore* store = iSmsMtm->Entry().ReadStoreL();
        CleanupStack::PushL(store);

        if (store->HasBodyTextL())
            {
            CRichText* richText = CRichText::NewL(
                              CEikonEnv::Static()->SystemParaFormatLayerL(),
                              CEikonEnv::Static()->SystemCharFormatLayerL());
            richText->Reset();
            CleanupStack::PushL(richText);

            // Get the SMS body text.
            store->RestoreBodyTextL(*richText);
            const TInt length = richText->DocumentLength();
            TBuf<KMessageBodySize> message;

            // Check length because message is read to limited size TBuf.
            if ( length >= KMessageBodySize )
                {
                message.Append( richText->Read(0, KMessageBodySize -1) );
                }
            else
                {
                message.Append( richText->Read(0, length) );
                }

            aMessage.Append( message );

            CleanupStack::PopAndDestroy(richText);
            }
        CleanupStack::PopAndDestroy(store);
        }
    else
        {
        return EFalse;
        }

    return ETrue;
    }

// Get beginning of message's body. Index entry is just a summary of the whole
// message.
TBool CSMSExampleMtmsEngine::GetMessageIndexBodyTextL( TMsvId aMessageId,
                                                       TDes& aMessage)
    {
    iSmsMtm->SwitchCurrentEntryL( aMessageId );

    aMessage.Append(iSmsMtm->Entry().Entry().iDescription );
    return ETrue;
    }


// Copy message to another folder.
void CSMSExampleMtmsEngine::CopyMessageL( TMsvId aMessageId, TMsvId aFolder )
    {
    iSmsMtm->SwitchCurrentEntryL( aMessageId );
    TMsvSelectionOrdering selection;
    selection.SetShowInvisibleEntries(ETrue);
    CMsvEntry* parentEntry = CMsvEntry::NewL( iSmsMtm->Session(),
        iSmsMtm->Entry().Entry().Parent(), selection );
    CleanupStack::PushL(parentEntry);
    // iSmsMtm points to the parent
    parentEntry->CopyL( aMessageId, aFolder );
    CleanupStack::PopAndDestroy(); // parentEntry
    }

// ids of messages that has been got using ListMessagesL
RArray<TMsvId>* CSMSExampleMtmsEngine::GetMessageIds()
    {
    return iIdArray;
    }


// Get all folder's children which are SMS messages.
// Note that the folder can contain .sis files which have to be filtered out.
// IdArray is made here because it makes finding the SMS easier later on.
void CSMSExampleMtmsEngine::GetFolderSMSMessageInformationL(
                                                TMsvId aFolderID,
                                                CDesCArrayFlat*& aAddresses,
                                                CDesCArrayFlat*& aMessages )
    {

    iSmsMtm->SwitchCurrentEntryL( aFolderID );
    CMsvEntry& entry = iSmsMtm->Entry();

    // Remember to delete this entry after no longer needed!
    // Only intrested in messages. Filter out service etries.
    CMsvEntrySelection* entries = entry.ChildrenWithMtmL(KUidMsgTypeSMS);

    CDesCArrayFlat* arrayAddr =
                        new (ELeave) CDesCArrayFlat( KArrayGranularity );
    CDesCArrayFlat* arrayMsgBody =
                        new (ELeave) CDesCArrayFlat( KArrayGranularity );

    iIdArray = new (ELeave) RArray<TMsvId>;

    for (TInt i = 0; i < entries->Count(); i++ )
        {
        TBuf<KMessageBodySize> body;
        TBuf<KMessageAddressLength> address;

        // Append only SMS messages, .sis files etc. are disregarded.
        // Take only beginning of SMS body, because listbox only shows
        // beginning of message.
        if ( GetMessageIndexBodyTextL( (*entries)[i], body ) ) // SMS body
            {
            iIdArray->Append( (*entries)[i] );
            arrayMsgBody->AppendL ( body );

            // Recipient address
            //If done an own messafe to the drafts with out a address then failes
            TRAPD(err, GetMessageAddressL( (*entries)[i], address ) );
            if( err == KErrNone)
                arrayAddr->AppendL ( address );
            else
                arrayAddr->AppendL ( KSenderJohnDoe );
            }
        }

    // Delete entries. This is your responsibility.
    entries->Reset();
    delete entries;
    entries = 0;

    aAddresses = arrayAddr; // address array
    aMessages = arrayMsgBody; // msg body array
    }

// Tells when the session has been opened
void CSMSExampleMtmsEngine::HandleSessionEventL(TMsvSessionEvent aEvent,
                                                TAny* aArg1,
                                                TAny* aArg2,
                                                TAny* aArg3)
    {
#ifdef __SERIES60_3X__
    switch (aEvent)
        {
        // This event tells us that the session has been opened
        case EMsvServerReady:
            CreateMtmClientL();
            break;

        default:
            // do nothing
            break;
        }
#else
		switch ( aEvent )
		{
			// Session to server established
		case EMsvServerReady:
			{
				TMsvId serviceId( KUidMsgTypeSMS.iUid ); // SMS service id

				// Determine if the event was succesful.
				// ServiceProgress inserts TBuf8 value in progress.
				TBuf8<20> progress;
				iSession->ServiceProgress( serviceId, progress );
				_LIT8( KCompare, "KErrNone" );

				if ( progress.Compare( KCompare ) )
				{
					/*                HBufC* text = StringLoader::LoadLC( R_SMS_SERVER_SESSION );
					CAknInformationNote* informationNote =
					new ( ELeave ) CAknInformationNote;
					informationNote->ExecuteLD( *text );
					CleanupStack::PopAndDestroy( text );
					*/
					// Check that MtmRegistry has not already been accessed.
					if ( !iClientMtmReg  )
					{
						AccessMtmL();
					}
				}

				else
				{
					/*              HBufC* text = StringLoader::LoadLC( R_SMS_SESSION_ERROR );
					CAknErrorNote* errorNote = new ( ELeave ) CAknErrorNote;
					errorNote->ExecuteLD( *text );
					CleanupStack::PopAndDestroy( text );*/
				}

				break;
			}
/*
			// A new entry has been created on the message server.
		case EMsvEntriesCreated:
			{
				// Entry id is obtained from the session event arguments.
				TMsvId* entryId = STATIC_CAST( TMsvId*, aArg2 );

				// We are interested in messages that are created in Inbox.
				if ( *entryId != KMsvGlobalInBoxIndexEntryId )
				{
					break;
				}

				// We take the created entries into a selection
				CMsvEntrySelection* newEntries =
					STATIC_CAST( CMsvEntrySelection*, aArg1 );

				// Process each created entry.
				for ( TInt i( 0 ); i < newEntries->Count(); i++ )
				{
					// We are interested in SMS messages.
					if ( ( iSession->GetEntryL( newEntries->At( i ) ) )
						->Entry().iMtm == KUidMsgTypeSMS )
					{
						// Add the entry to the selection of all received messages.
						iEntrySelection->AppendL( newEntries->At( i ), 1 );

						// Set received messages visible.
						//MessageReceivedL( newEntries->At( i ) );
					}
				}

				break;
			}
*/
		case EMsvCloseSession:
		case EMsvServerTerminated:
		case EMsvGeneralError:
		case EMsvServerFailedToStart:
			{
				//            iSmsAppUi->ServerDown( aEvent );    // close application
				break;
			}

			// All other events are ignored.
		default:
			break;
		}
#endif
    }
